#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>

#define CLOSE_CMD _IO(0x02,1)
#define OPEN_CMD _IO(0x02,2)
#define MODIFY_CMD _IO(0x02,3)

static int fd;


void signal_func(int num)
{
    char buff = 0;
    read(fd,&buff,1);
    printf("read data:%d\r\n",buff);
}


int main(int argc ,char *argv[])
{
     fd = open("/dev/mykey",O_RDWR);  int op_mod = 0;
    int fd_led = open("/dev/gpioLED",O_RDWR); 
    if(fd<0)
    {
        printf("open error!\r\n");
        return -1;
    }

    fd_set set;

    int ret = 0;

    if(op_mod==2)
    {
        signal(SIGIO,signal_func);
        fcntl(fd, F_SETOWN, getpid());
        int flags = fcntl(fd, F_GETFL, 0);
        fcntl(fd, F_SETFL, flags|FASYNC);
    }

    while(1)
    {
        if(op_mod == 0)
        {
            FD_ZERO(&set);
            FD_SET(fd,&set);
            struct timeval t;
            t.tv_sec=1;
            t.tv_usec=0;
            
            ret  = select(fd+1,&set,NULL,NULL,&t);//这里的select底层去调自己写的poll
            //每个fd都有自己的poll，内核会轮询调用一圈poll，哪个返回了fd集合需要的标志，select会返回
            //有fd准备好了，如果设置了超时，则加入到等待队列头，没有fd准备好的情况下select不返回，因为
            //超时了/没有设置超时也算准备好了，就算设置了超时，内核也要不断的轮询执行poll判断是否准备好，
            //好判断是否需要唤醒进程，不是给每个fd设置超时，而是给进程设置超时
            //在我的驱动里，poll返回可读之后，select设置了超时，所以会立即唤醒进程，进行read读，poll返回可读
            //了那么read里的自定义状态肯定成立，如果设置超时则会立即返回
            //三个：可读、可写、是否异常，这三个集合中任何一个fd准备好都将立即返回
            //poll_wait逻辑上其实是把当前进程加入到内核的poll指定的等待队列，问题来了，每个fd都有poll
            //那么一个进程被放入多个等待队列，是否会影响select的立即返回？不会，原因如下：
            //内核会管理每个进程的资源，包括它们所在的等待队列。当进程被唤醒时，它会从所有等待队列中移除，类似于
            //“与/或”的条件判断问题，一旦 select 检测到某个文件描述符已经变得 可读 或 可写，它会立即返回，
            //不会继续阻塞，即便进程在其他条件（例如 可写）的队列中。这是select的机制：某个描述符准备好
            //对应的驱动就从那个等待队列头里唤起进程，所以等待队列头仅仅是为了方便内核的驱动反推出该唤醒的是哪个进程，
            //而实际的阻塞如等待超时，是在select里并非由等待队列阻塞，而这么多描述符的等待队列肯定有机制快速清理，不然多次
            //轮询执行poll，次次都挂上？肯定是当次用完立即清理了！不然为什么内核的poll实现必须先挂上再判断返回标志？
            //如果不是当次用完立即清理，那就有可能在阻塞模式调用select出现冲突->select返回了，但是仍然留在等待队列
            //从而影响到阻塞模式的read使用等待队列，read条件满足了仍然继续阻塞，因为中断早已结束了，而我从中断调用weak
            //分析一顿下来似乎也不影响，只要驱动严格根据状态处理阻塞(即从等待队列头添加/删除)，这一点也不会出问题
            printf("ret=%d\r\n",ret);
            switch (ret)
            {
            case 0:
                printf("time out !\r\n");
                break;
            case -1:
                printf("error\r\n");
                break;
            default:
                if(FD_ISSET(fd,&set))
                {
                    char buff;
                    read(fd,&buff,1);
                    printf("read data:%d\r\n",buff);
                }
                break;
            }
        }
        else if(op_mod == 1)
        {
            char buff = 0;
            ret = read(fd,&buff,1);
            if(ret>=0)
                printf("read data:%d\r\n",buff);
        }
        else if(op_mod == 2)
        {
            sleep(1);
        }
        
    }
    close(fd);
    close(fd_led);
    return 0;
}